# Communications module for CROSS

#########################################################################################
#
# Copyright (C) 2008  
# Authors: Aljosa Osep (aljosa.osep@gmail.com), Jernej Kranjec (jernej.kranjec@gmail.com)
# From Faculty of Electrical Engineering and Computer Science, Maribor, Slovenia
# Done at School of Technology, Seinajoki, Finland
# Under mentor Kimmo Salmenjoki (kimmo.salmenjoki@seamk.fi)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#########################################################################################

# ==================== imports ====================== #
import serial		# import pyserial
import exceptionModule	# import our exception module

import sys

############################
# Communicator class
############################
class Communicator:	# class definition
	global s	# global serial instance
	global input	# user input

	# function send data to serial port
	def sendData(self, input):
		if input.startswith('}') == False:			# command should start with '}' sign
			self.s.write('}' + input)			# if it doesn't, add sign manually and send input to device
		else:
			self.s.write(input)  				# otherwise, just send data to port

	# retrieves data from serial port
	def retriveData(self):
		self.answer = ""					# answer buffer
		self.s.readline()					# read first (irelevant) line
		
		line = ""	
		while line != '{':					# also, skip all charascter unstill '{', which is
			line = self.s.read(1)				# the start of actual answer

		while True:						# loop
			if line == '\r':				
				self.answer = self.answer + '\n'
			elif line == "":				# if there is empty space
				break					# stop the loop, because we have got an answer
			else:
				self.answer = self.answer + line	# add character to buffer
			line = self.s.read(1)				# read one character from port
		return self.answer					# return answer buffer

	def configureConnection(self, port=0, timeout=0.1, xonoff=0, rtscts=0, baudrate=9600, parity=serial.PARITY_NONE, bytesize=serial.EIGHTBITS, stopbits=serial.STOPBITS_ONE):
		self.port = port
		self.timeout = timeout
		self.xonoff = xonoff
		self.rtscts = rtscts
		self.baudrate = baudrate
		
		self.parity = parity
		self.bytesize = bytesize
		self.stopbits = stopbits

	def valueToSerialSetting(self, val1, val2, val3):
			if val1 == 1:					# parity			
				self.parity = serial.PARITY_NONE	# val 1 means NONE
			if val1 == 2:
				self.parity = serial.PARITY_EVEN	# val 2 means EVEN
			if val1 == 3:
				self.parity = serial.PARITY_ODD		# val 3 means ODD

			if val2 == 1:					# bits num
				self.bytesize = serial.FIVEBITS		# val 1 means NONE
			if val2 == 2:
				self.bytesize = serial.SIXBITS		# val 2 means SIXBITS
			if val2 == 3:
				self.bytesize = serial.SEVENBITS	# val 3 means SEVENBITS
			if val2 == 4:
				self.bytesize = serial.EIGHTBITS	# val 4 means EIGHTBITS

			if val3 == 1:					# stop bits
				self.stopbits = serial.STOPBITS_ONE	# val 1 == one stopbit
			if val3 == 2:
				self.stopbits = serial.STOPBITS_TWO	# val 3 == two stopbits

	def serialSettingToValueParity(self):
		if self.parity == serial.PARITY_NONE:
			return 1
		elif self.parity == serial.PARITY_EVEN:
			return 2
		elif self.parity == serial.PARITY_ODD:
			return 3

	def serialSettingToValueBytesize(self):
		if self.bytesize == serial.FIVEBITS:				# val 1 means NONE
			return 1
		elif self.bytesize == serial.SIXBITS:				# val 2 means SIXBITS
			return 2
		elif self.bytesize == serial.SEVENBITS:				# val 3
			return 3
		elif self.bytesize == serial.EIGHTBITS:				# val 4
			return 4

	def serialSettingToValueStopbits(self):
		if self.stopbits == serial.STOPBITS_ONE:			# val 1 == one stopbit
			return 1
		if self.stopbits == serial.STOPBITS_TWO:			# val 1 == one stopbit
			return 2

	def configureFromFile(self):
		f = open("crosscfg.conf", 'r')
		try:
			self.port = int(f.readline())				# read and parse port value
			self.timeout = float(f.readline())			# read and parse timeout value
			self.xonoff = int(f.readline())				# read and parse xonoff value
			self.rtscts = int(f.readline())				# read and parse rtscts value
			self.baudrate = int(f.readline())			# read and parse baudrate value
		
			parity = int(f.readline())				# read and parse parity value
			bytesize = int(f.readline())				# read and parse bytesize value
			stopbits = int(f.readline())				# read and parse stopbits value

			self.valueToSerialSetting(parity, bytesize, stopbits)

		except ValueError:
			print "Error! It seems that config file is corrupted. Loading default settings."
			self.configureConnection()

		except IOError:
			print "Error! Problem with opening file. Loading default settings."
			self.configureConnection()
		finally:
			f.close()


	def configureToFile(self, port, timeout, xonoff, rtscts, baudrate, parity, bytesize, stopbits):
		f = open("crosscfg.conf", 'w')
		try:
			# write settings to class
			self.port = int(port)
			self.timeout = float(timeout)
			self.xonoff = int(xonoff)
			self.rtscts = int(rtscts)
			self.baudrate = int(baudrate)
			self.valueToSerialSetting(parity, bytesize, stopbits)

			# write settings to file
			f.write(port+'\r\n')
			f.write(timeout+'\r\n')
			f.write(xonoff+'\r\n')
			f.write(rtscts+'\r\n')
			f.write(baudrate+'\r\n')
			f.write(str(parity)+'\r\n')
			f.write(str(bytesize)+'\r\n')
			f.write(str(stopbits)+'\r\n')

		except IOError:
			print "Error! Problem with writing on disk. Configuration will not be written."
		except ValueError:
			print "Error! It seems that you are trying write an invalid configuration."
		finally:
			f.close()
		
		
	# connection opening
	#---------------------
	# config=True - settings are loaded from config
	# config=False -
	# parity: 1-PARITY_NONE, 2-PARITY_EVEN, 3-PARITY_ODD
	# bytesize: 1-FIVEBITS, 2-SIXBITS, 3-SEVENBITS, 8-EIGHTBITS
	# stopbits: 1-ONE, 2-TWO
	def open_connection(self, reopen=False, config=True, port=0, timeout=0.1, xonoff=0, rtscts=0, baudrate=9600, parity=1, bytesize=4, stopbits=1):
		if reopen == True:					# we are resetting the connection
			if self.s.isOpen() == True:			# so we check if connection is open alredy
				self.s.close()				# if it is, we close connection

		if config == True:					# settings from config file?
			self.configureFromFile()			# yes - configure from file
		else:															
			self.configureConnection(port, timeout, xonoff, rtscts, baudrate)
			self.valueToSerialSetting(parity, bytesize, stopbits)
		# try to open the port
		try:
			self.s = serial.Serial(port=self.port, parity=self.parity,
			  		bytesize=self.bytesize,
			  		stopbits=self.stopbits,
			  		timeout=self.timeout,
			  		xonxoff=self.xonoff,
			  		rtscts=self.rtscts,
	      		  		baudrate=self.baudrate)

		except serial.SerialException:				# unable to open port
		#	self.open_connection() # default
			raise exceptionModule.SerialPortException	# raise our serialport exception


	# class initialization
	def __init__(self, config=True, port=0, timeout=0.1, xonoff=0, rtscts=0, baudrate=9600, parity=1, bytesize=4, stopbits=1):
		self.open_connection(False, config, port, timeout, xonoff, rtscts, baudrate, parity, bytesize, stopbits)


	# class deinitialization
	def __del__(self):						# destructor
		if self.s.isOpen() == True:				# is serial connection is still open
			self.s.close()					# close it

